package com.apps.java.javanote02;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class _27反射 extends Object {
    /**
     *  反射（Java Reflection）
     *      一、静态和动态
     *          1.动态语言
     *              是一类在运行时可以改变其结构的语言：例如新的函数、对象、甚至代码可以被引进，
     *              已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代码可以根据
     *              某些条件改变自身结构。
     *              主要的语言：Object-C、C#、JavaScript、PHP、Python等
     *          2.静态语言
     *              与动态相对应的，运行时结构不可变的语言就是静态语言。例如：Java、C、C++。
     *              java不是动态语言，但java可以称为“准动态语言”，即java有一定的动态性，我们
     *              可以利用反射机制获得类似动态语言的特性。Java的动态性让编程的时候更加灵活。
     *
     *      二、Java反射机制概述
     *          1.Reflection（反射）是java被视为动态语言的关键，反射机制允许程序在执行期借助
     *            于Reflection API获得任何类的内部信息，并能直接操作任意对象的内部属性及方法。
     *              Class c = Class.forName("java.lang.String)
     *          2.加载完类后，在堆内存的方法区中就产生了一个Class类型的对象（一个类只有一个CLa
     *            ss对象），这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结
     *            构。这个对象就像一面镜子，透过这个镜子看到类的结构，所以，我们形象的称之为：
     *            反射
     *          3.正常方式：引入需要的“包类”名称 -> 通过new实例化 -> 取得实例化对象
     *            反射方式：实例化对象 -> getClass()方法 -> 得到完整的“包类：名称
     *
     *      三、java反射机制提供的功能
     *          1.在运行时判断任意一个对象所属的类
     *          2.在运行时构造任意一个类的对象
     *          3.在运行时判断任意一个类所具有的的成员变量和方法
     *          4.在运行时获取泛型信息
     *          5.在运行时调用任意一个对象的成员变量和方法
     *          6.在运行时处理注解
     *          7.生成动态代理
     *          8.···
     *
     *      四、反射的优缺点
     *          优点：可以实现动态创建对象和编译，体现出很大的灵活性
     *          缺点：对性能有影响。使用反射基本上是一种解释操作，我们可以告诉JVM，我们希望做什么
     *                并且它满足我们的要求。这类操作总是慢于直接执行相同的操作。
     *
     *      五、反射相关的主要API
     *          1.java.lang.Class：代表一个类
     *          2.jaa.lang.reflect.Method：代表类的方法
     *          3.java.lang.reflect.Field：代表类的成员变量
     *          4.java.lang.reflect.Constructor：代表类的构造器
     *          5.···
     *
     *      六、Class类
     *          1.在Object类中定义了一下的方法，此方法将被所有子类继承
     *              public final Class getClass()
     *          2.以上的方法返回值的类型是一个Class类，此类是java反射的源头，实际上所谓反射从程序的
     *          3.运行结构来看也很好理解，即：可以通过对象反射求出类的名称。
     *          4.对象照镜子后可以得到的信息：某个类的属性、方法和构造器、某个类到底实现了哪些接口。
     *            对于每个类而言，JRE都为其保留一个不变的Class类型的对象。一个Class对象包含了特定
     *            某个结构（class/interface/enum/annotation/primitive type/void/[])的有关信息。
     *            （1）Class本身也是一个类
     *            （2）Class对象只能由系统建立对象
     *            （3）一个加载的类在JVM中只会有一个Class实例
     *            （4）每个类的实例都会记得自己是由哪个Class实例所生成
     *            （5）通过Class可以完整地得到一个类中的所有被加载的结构
     *            （6）Class类是Reflection的根源，针对任何你想动态加载、运行的类，唯有先获得相应的
     *                 Class对象。
     *
     *      七、Class类的常用方法
     *          static ClassforName(String name)：   返回指定类名name的Class对象
     *          Object newInstance()：               调用缺省构造函数，返回Class对象的一个实例
     *          getName()：                          返回此Class对象所表示的实体（类，接口，数组类或void）的名称
     *          Class getSuperClass()：              返回当前Class对象的父类的Class对象
     *          Class[] getinterfaces()：            返回当前Class对象的接口
     *          ClassLoader getClassLoader()：       返回该类的类加载器
     *          Constructor[] getConstructors()：    返回一个包含某些Constructor对象的数组
     *          Method getMethod(String name,Class..T):返回一个Method对象，此对象的形参类型为paramType
     *          Field[] getDeclareFields()：         返回Field对象的一个数组
     *
     *      八、获取Class类的实例
     *          1.若已知具体的类，通过类的class属性获取，该方法最为安全可靠，程序性能最高
     *              Class clazz = Person.class;
     *          2.已知某个类的实例，调用该实例的getClass()方法获取Class对象
     *              Class clazz = person.getClass();
     *          3.已知一个类的全类名，且该类在类路径下，可通过Class类的静态方法forName()获取，
     *            可能抛出ClassNotFoundException
     *              Class clazz = Class.forName("demo01.Student");
     *          4.内置基本数据类型可以直接使用类名.Type
     *          5.还可以利用ClassLoader
     *
     *      九、哪些类型可以有CLass对象？
     *          class：外部类，成员（成员内部类，静态内部类），局部内部类，匿名内部类。
     *          interface：接口
     *          []：数组
     *          enum:枚举
     *          annotation：注解@interface
     *          primitive type:基本数据类型
     *          void
     *
     *      十、类的加载与ClassLoader的理解
     *          1.加载：将class文件字节码内容加载到内存中，并将这些静态数据换成方法区的运行时
     *                 数据结构，然后生成一个代表这个类的java.lang.Class对象.
     *          2.链接：将java类的二进制代码合并到JVM的运行状态之中的过程。
     *                  验证：确保加载的类信息符合JVM规范，没有安全方面的问题
     *                  准备：正式为类变量(static)分配内存并设置类变量默认初始化的阶段，这些
     *                        内存都将在方法区中进行分配。
     *                  解析：虚拟机常量池内的符号引用（常量名）替换为直接引用（地址）的过程。
     *          3.初始化：
     *              .执行类构造器<clinit>()方法的过程。类构造器<clinit>()方法是由编译期自动收
     *               集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。（类构造器是构造
     *               类信息的，不是构造该类对象的构造器）。
     *              .当初始化一个类的时候，如果发现其父类还没有进行初始化，则需要先触发其父类的
     *               初始化。
     *              .虚拟机会保证一个类的<clint>()方法在多线程环境中被正确加锁和同步。
     *
     *      十一、什么时候会发生类初始化？
     *          类的主动引用（一定会发生类的初始化）
     *              1.当虚拟机启动，先初始化main方法所在的类
     *              2.new一个类的对象
     *              3.调用类的静态成员（除了final常量）和静态方法
     *              4.使用java.lang.reflect包的方法对类进行反射调用
     *              5.当初始化一个类，如果其父类没有被初始化，则会先初始化它的父类
     *          类的被动引用
     *              1.当访问一个静态域时，只有真正声明这个域的类才会被初始化。如：当通过子类引
     *                用父类的静态变量，不会导致子类初始化
     *              2.通过数组定义类引用，不会触发此类的初始化
     *              3.引用常量不会触发此类的初始化（常量在链接阶段就存入调用诶的常量池中了）
     *
     *      十二、类加载器的作用
     *          1.类加载的作用：将class文件字节码内容加载到内存中，并将这些静态数据转换成方法
     *            区的运行时数据结构，然后在堆中生成一个代表这个类的java.lang.Class对象，作
     *            为方法区中类数据的访问入口。
     *          2.类缓存：标准的 JavaSE类加载器可以按要求查找类，但一旦某个类被加载到类加载器
     *            中，它将维持加载（缓存）一段时间。不过JVM垃圾回收机制可以回收这些Class对象。
     *          类加载器作用的用来把类（class）装载进内存的。JVM 规范定义了如下类型的类的加载器。
     *              1.引导类加载器：用C++编写的，是JVM子代的类加载器，负责Java平台核心库，用来
     *                装载核心类库。该加载器无法直接获取
     *              2.扩展类加载器：负责jre/lib/ext目录下的jar包或-D java.ext.dirs指定目录下
     *                的jar包装入工作库。
     *              3.系统类加载器：负责java-classpath或-D java.class.path所指的目录下的类与
     *                jar包装入工作，是最常用的加载器
     *
     *      十三、创建运行时类的对象
     *          1.获取运行时类的完整结构
     *              通过反射获取运行时类的完整结构
     *                  Field、Method、Constructor、Superclass、Interface、Annotation
     *
     *      十四、Class对象能做什么
     *          创建类的对象：调用Class对象的newInstance()方法
     *              1.类必须优一个而无参数的构造器
     *              2.类的构造器的访问权限需要足够
     *          思考：难道没有无参的构造器就不能创建对象了吗？
     *          回答：只要在操作的时候明确的调用类中的构造器，并将参数传递进去之后，才可以实例化操作。
     *          步骤如下：
     *              1.通过Class类的getDeclaredConstructor(Class..parameterTypes)取得本类的指定
     *                形参类型的构造器。
     *              2.向构造器的形参中传递一个对象数组进去，里面包含了构造器中所需的各个参数。
     *              3.通过Constructor实例化 对象。
     *
     *          调用指定的方法：通过反射，调用类中的方法，通过Method类完成。
     *              1.通过Class类的getMethod(String name,Class...parameterTypes)方法取得一个Mthod对
     *                象，并设置此方法操作时所需的参数类型。
     *              2.之后使用Object invoke(Object obj,Object[] args)进行调用，并向方法中
     *               传递要设置的obj对象的参数信息
     *                  .Object 对应原方法的返回值，若原方法无返回值，此时返回null。
     *                  .若原方法若为静态方法，此时形参Object obj可为null。
     *                  .若原方法形参列表为空，则Object[] args为null。
     *                  .若原方法声明为private，则需要在调用此invoke()方法前，显式调用方法对象的setAccessible(true)
     *                   方法，将可访问private的方法。
     *              3.setAccessible：
     *                  .Method和Field、Constructor对象都有setAccessible()方法。
     *                  .setAccessible作用是启动和禁用访问安全检查的开关。
     *                  .参数值为true则指示反射的对象在使用时应该取消java语言访问检查。
     *                      1.提高反射的效率。如果代码中必须用反射，而该句代码需要频繁的被调用，那么请设置为true.
     *                      2.使得原本无法访问的私有成员也可以访问
     *                  .参数值为false则指示反射的对象应该实施java语言访问检查。
     *
     *          十五、反射操作泛型
     *              1.java采用泛型擦除的机制来引入泛型，Java中的泛型仅仅是给编译器javac使用的，确保数据的安全型和
     *                免去强制类型转换问题，但是，一旦编译完成，所有和泛型有关的类型全部擦除
     *              2.为了通过反射操作这些类型，java新增了ParameterizedType, GenericArrayType，TypeVariable和
     *                WildcardType几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型。
     *                  .ParameterizedType：表示一种参数化类型，比如Collection<String>
     *                  .GenericArrayType：表示一种元素类型是参数化类型或者类型变量的数组类型。
     *                  .TypeVariable：是各种类型变量的公共父接口
     *                  .WildcardType：代表一种通配符类型表达式
     *
     *          十六、反射操作注解
     *              1.了解什么是ORM：
     *                  Object relationship Mapping 对象关系映射
     *                      Class Student{
     *                          int id;                     id    name    age
     *                          String name;        ->      001   张三    27
     *                          int age;                    002   李四    25
     *                      }
     *                      （1）类和表结构对应
     *                      （2）属性和字段对应
     *                      （3）对象和记录对应
     */
    static{
        System.out.println("main方法所在的类被加载");
    }

    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
        System.out.println("---------------------");
    /*

    //通过反射获取类的class对象
        Class<?> c1 = Class.forName("com.apps.java.javanote02.User");
        System.out.println(c1);

        //一个类在内存中只有一个Class对象
        //一个类被加载后，类的整个结构都会被封装在Class对象中。
        Class<?> c2 = Class.forName("com.apps.java.javanote02.User");
        Class<?> c3 = Class.forName("com.apps.java.javanote02.User");
        Class<?> c4 = Class.forName("com.apps.java.javanote02.User");

    //测试Class类的创建方法有哪些
        Person per = new Studens();
        System.out.println("这个人是：" + per.name);

        //方式一：通过对象获得
        Class<?> perC = per.getClass();

        //方式二：通过forName获得
        Class<?> perC2 = Class.forName("com.apps.java.javanote02.Person");

        //方式三：通过类名.class
        Class<Studens> stuC = Studens.class;

        //方式四：基本内置类型的包装类都有一个Type属性
        Class inT = Integer.TYPE;

        //获得父类类型
        Class<?> suC = c1.getSuperclass();

    //类的加载
        /*
            1.加载到内存，会产生一个类对应的Class对象
            2.链接，链接结束后 num=0
            3.初始化
                <clinit>(){
                        System.out.println("A类静态代码块初始化");
                        num = 30;
                        num = 100;
                }
                num = 100
         //
        //创建A类对象
        A a = new A();
        System.out.println(a.num);

        System.out.println("----------------");

    //类初始化
        //1.类的主动引用
//        Son son = new Son();
//        System.out.println(son.n);

        //反射也会产生主动引用
//        Class<?> sonC = Class.forName("com.apps.java.javanote02.Son");

        //2.不会产生类的主动引用
        //子类调用父类静态成员，子类不会被加载
//        System.out.println(Son.b);
        //通过数组定义类引用，不会触发此类的初始化
        Son[] array = new Son[10];
        //引用常量不会触发此类的初始化
        System.out.println(Son.M);

        System.out.println("----------------");

    //获得类加载器
        //获得系统类加载器
        ClassLoader syCL = ClassLoader.getSystemClassLoader();
        System.out.println(syCL);

        //获取系统类加载器的父类加载器 -> 扩展类加载器
        ClassLoader parent = syCL.getParent();
        System.out.println(parent);

        //获取扩展类加载器的父类加载器 ->引导类加载器
        ClassLoader parentP = parent.getParent();
        System.out.println(parentP);//null

        //测试当前类是哪个加载器加载的
        ClassLoader classLoader = Class.forName("com.apps.java.javanote02._27反射").getClassLoader();
        System.out.println(classLoader);

        //测试JDK内置的类是哪个加载器加载的
        ClassLoader objectLoader = Class.forName("java.lang.Object").getClassLoader();
        System.out.println(objectLoader);

        System.out.println("----------------");
        //如何获得系统类（引导类）加载器可以加载的路径
        System.out.println(System.getProperty("java.class.path"));

        System.out.println("-----------------------");

    //获取运行时类的完整结构
        Class<?> uC = Class.forName("com.apps.java.javanote02.User");

//        //创建对象
//        User u = new User();
//        //通过对象反射获取Class对象
//        Class<? extends User> uC = u.getClass();

        //获得类的名字
        System.out.println("User全限定类名：" + uC.getName());
        //获得类的简单名字
        System.out.println("User类名：" + uC.getSimpleName());

        System.out.println("---------------");

        //获得类的属性
        System.out.println("获得类的属性");

        Field[] uF = uC.getFields();//只能找到public属性
        for (Field field : uF) {
            System.out.println(field);
        }

        uF = uC.getDeclaredFields();//找到全部属性
        for (Field field : uF) {
            System.out.println(field);
        }

        Field name = uC.getDeclaredField("name");
        System.out.println("name属性：" + name);

        System.out.println("-------------------");

        //获得类的方法
        System.out.println("获得类的方法");

        Method[] uM = uC.getMethods();//获得本类及其父类的全部public方法
//        for (Method method : uM) {
//            System.out.println(method);
//        }

        uM = uC.getDeclaredMethods();//获得本类的所有方法
//        for (Method method : uM) {
//            System.out.println(method);
//        }

        Method uGN = uC.getMethod("equals", Object.class);
        System.out.println("正常的：" + uGN);

        uGN = uC.getDeclaredMethod("getAge");
        System.out.println("加Declared：" + uGN);

        System.out.println("----------------");

        //获得构造器
        System.out.println("获得构造器：");

        Constructor[] uCons = uC.getConstructors();
        System.out.println("获得所有构造器：");
        for (Constructor<?> uCon : uCons) {
            System.out.println(uCon);
        }

        uCons = uC.getDeclaredConstructors();
        System.out.println("获得本类所有构造器：");
        for (Constructor<?> uCon : uCons) {
            System.out.println(uCon);
        }

        System.out.println("获得指定构造器：" + uC.getConstructor(String.class,int.class,int.class));
    */
        
    //通过反射，动态的创建对象    
        //获得Class对象
        Class<?> aClass = Class.forName("com.apps.java.javanote02.User");

        //构造一个对象
//        User user = (User) aClass.newInstance();//本质是调用了类的无参构造器
//        System.out.println(user);

        //通过构造器创建对象
        System.out.println("通过构造器创建对象");
        Constructor<?> constructor = aClass.getDeclaredConstructor(String.class, int.class, int.class);
        User users = (User) constructor.newInstance("张三", 1, 18);
        System.out.println(users);

        System.out.println("---------------------");

        //通过反射调用方法
        System.out.println("通过反射调用方法");
        Method setName = aClass.getMethod("setName", String.class);
        //invoke：激活的意思
        //(对象,"方法的值")
        setName.invoke(users, "王五");
        System.out.println(users.getName());

        System.out.println("-----------------");

        //通过反射操作属性
        System.out.println("通过反射操作属性");
        Field name = aClass.getDeclaredField("name");

        //不能直接操作私有属性，我们需要关闭程序的安全检测，setAccessible设为true
        name.setAccessible(true);
        name.set(users,"吴亦凡");
        System.out.println(users.getName());


    }
}

//实体类：pojo,entity
class User{
    private String name;
    private int id;
    private int age;

    public User() {
    }

    public User(String name, int id, int age) {
        this.name = name;
        this.id = id;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", id=" + id +
                ", age=" + age +
                '}';
    }
}

class Person{
    public String name;

    public Person(String name) {
        this.name = name;
    }

    public Person() {
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }
}

class Studens extends Person{

    public Studens(){
        this.name = "何锟大傻狗";
    }
}

class Teacher extends Person{
    public Teacher(){
        this.name = "何锟还是一条大大大大傻狗";
    }
}

//类的加载
class A{

    static {
        System.out.println("A类静态代码块初始化");
        num = 30;
    }

    static int num = 100;

    public A() {
        System.out.println("A类的无参构造初始化");
    }
}

//类初始化
class Father{
    static int b = 2;

    //static代码块
    static {
        System.out.println("父类被加载");
    }
}
class Son extends Father{

    static {
        System.out.println("子类被加载");
        n = 520;
    }

    static int n = 100;

    static final int M = 1;

}

